package org.gd.hellocrypt.rsa;

import java.io.File;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;

public class JcaKeyPair {

    private final File privKeyFile;

    private final File pubKeyFile;

    private int keySize;

    public JcaKeyPair(int keySize, String output, String algorithm) throws Exception
    {
        this.privKeyFile = new File(output + ".pri");
        this.pubKeyFile = new File(output + ".pub");
        System.out.println("in JcaKeyPair constructor");
        KeyPairGenerator kg = KeyPairGenerator.getInstance(algorithm);
        kg.initialize(keySize);
        System.out.println();
        System.out.println("KeyPairGenerator Object Info: ");
        System.out.println("Algorithm = " + kg.getAlgorithm());
        System.out.println("Provider = " + kg.getProvider());
        System.out.println("Key Size = " + keySize);
        KeyPair pair = kg.generateKeyPair();
        System.out.println("Key Size = " + keySize);
        PrivateKey priKey = pair.getPrivate();
        PublicKey pubKey = pair.getPublic();
        DataOutputStream out = new DataOutputStream(new FileOutputStream(privKeyFile));
        byte[] ky = priKey.getEncoded();

        out.write(ky);
        out.close();
        System.out.println();
        System.out.println("Private Key Info: ");
        System.out.println("Algorithm = " + priKey.getAlgorithm());
        System.out.println("Saved File = " + privKeyFile.getCanonicalPath());
        System.out.println("Size = " + ky.length);
        System.out.println("Format = " + priKey.getFormat());
        out = new DataOutputStream(new FileOutputStream(pubKeyFile));
        ky = pubKey.getEncoded();
        out.write(ky);
        out.close();
        System.out.println();
        System.out.println("Public Key Info: ");
        System.out.println("Algorithm = " + pubKey.getAlgorithm());
        System.out.println("Saved File = " + pubKeyFile.getCanonicalPath());
        System.out.println("Size = " + ky.length);
        System.out.println("Format = " + pubKey.getFormat());

        this.keySize = keySize;
    }

    public void encrypt() throws Exception
    {
        System.out.println("in Encrypt");
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        System.out.println("1");

        DataInputStream pubKeyStream = new DataInputStream(new FileInputStream(pubKeyFile));
        System.out.println("2");
        byte[] pubKeyBytes = new byte[this.keySize]; // 1024 bits
        System.out.println("3");

        pubKeyStream.read(pubKeyBytes);
        System.out.println("4");
        pubKeyStream.close();
        System.out.println("5");
        X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(pubKeyBytes);
        System.out.println("6");
        PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
        System.out.println("7");

        Cipher cf = Cipher.getInstance("RSA/ECB/NoPadding");
        System.out.println("8");
        cf.init(Cipher.ENCRYPT_MODE, pubKey);
        System.out.println("9");
        byte[] data = new byte[this.keySize / 8];
        byte[] cipher = new byte[this.keySize];

        DataInputStream fis = new DataInputStream(new FileInputStream("Source.txt"));
        fis.read(data);

        cipher = cf.doFinal(data);
        System.out.println("10");
        DataOutputStream fos = new DataOutputStream(new FileOutputStream("Data.enc"));
        fos.write(cipher);
    }

    public void decrypt() throws Exception
    {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        FileInputStream priKeyStream = new FileInputStream(privKeyFile);
        byte[] priKeyBytes = new byte[this.keySize];
        priKeyStream.read(priKeyBytes);
        priKeyStream.close();
        PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(priKeyBytes);
        PrivateKey priKey = keyFactory.generatePrivate(priKeySpec);

        Cipher cf = Cipher.getInstance("RSA/ECB/NoPadding");
        cf.init(Cipher.DECRYPT_MODE, priKey);

        byte[] cipher = new byte[this.keySize / 8];
        byte[] plain = new byte[this.keySize];

        FileInputStream fis = new FileInputStream("Data.enc");
        fis.read(cipher);

        plain = cf.doFinal(cipher);

        FileOutputStream fos = new FileOutputStream("Result.txt");
        fos.write(plain);
    }
    public static void main(String[] args) throws Exception
    {
        System.out.println("in main");
        int keySize = 1024; // 1024 bits, 128 bytes
        String output = "RSA";
        String algorithm = "RSA"; // RSA
        JcaKeyPair jcaKeyPair = new JcaKeyPair(keySize, output, algorithm);
        jcaKeyPair.encrypt();
        jcaKeyPair.decrypt();
    }
}
